#include "drv_spi.h"
#include "stm32f10x_spi.h"

#define SPI_WAIT_TIMEOUT            ((uint16_t)0xFFFF)


#ifndef __USE_SOFT_SPI_INTERFACE__                                          /** 硬件SPI */

/*
    SPI1重映射，SPI原引脚为PA4 PA5 PA6 PA7 被OLED占用，所以重映射到PA15 PB3 PB4 PB5
    但PB3 PB4 PA13 PA14 PA15中PA15 PB3 PB4被JTAG占用，PA13 PA14被SWD占用，所以关闭
    JTAG,而只使用SWD，恰好可以用SPI1为通信
*/
void drv_spi_init( void )
{
    SPI_InitTypeDef         SpiInitStructer;
    GPIO_InitTypeDef        SpiGpioInitStructer;                            //初始化所使用的结构体

    //打开SPI各引脚的时钟
    RCC_APB2PeriphClockCmd( SPI_CLK_GPIO_CLK | SPI_MISO_GPIO_CLK | SPI_MOSI_GPIO_CLK | SPI_NSS_GPIO_CLK, ENABLE );

    RCC_APB2PeriphClockCmd( RCC_APB2Periph_AFIO, ENABLE );                  //使能AFIO时钟，这样才能重映射
    GPIO_PinRemapConfig( GPIO_Remap_SWJ_JTAGDisable, ENABLE);               //关闭JTAG，这样才能腾出PA15,PB3,PB4
    GPIO_PinRemapConfig( GPIO_Remap_SPI1, ENABLE);                          //将SPI1，重映射

    SpiGpioInitStructer.GPIO_Mode = GPIO_Mode_AF_PP;                        //SCK MOSI MISO 配置为复用推挽功能
    SpiGpioInitStructer.GPIO_Speed = GPIO_Speed_10MHz;
    
    SpiGpioInitStructer.GPIO_Pin = SPI_CLK_GPIO_PIN;
    GPIO_Init( SPI_CLK_GPIO_PORT, &SpiGpioInitStructer );                   //初始化SCK

    SpiGpioInitStructer.GPIO_Pin = SPI_MOSI_GPIO_PIN;
    GPIO_Init( SPI_MOSI_GPIO_PORT, &SpiGpioInitStructer );                  //初始化MOSI

    SpiGpioInitStructer.GPIO_Pin = SPI_MISO_GPIO_PIN;
    GPIO_Init( SPI_MISO_GPIO_PORT, &SpiGpioInitStructer );                  //初始化MISO

    SpiGpioInitStructer.GPIO_Mode = GPIO_Mode_Out_PP;                       //NSS配置为推挽输出
    SpiGpioInitStructer.GPIO_Pin = SPI_NSS_GPIO_PIN;
    GPIO_Init( SPI_NSS_GPIO_PORT, &SpiGpioInitStructer );                   //初始化NSS
    GPIO_SetBits( SPI_NSS_GPIO_PORT, SPI_NSS_GPIO_PIN );                    //置高

    SPI_I2S_DeInit( SPI_PORT );                                             //复位SPI

    if( SPI1 == SPI_PORT )
    {
        RCC_APB2PeriphClockCmd( SPI_PORT_CLK, ENABLE );                     //SPI1在APB2上，打开相应SPI时钟
    }
    else
    {
        RCC_APB1PeriphClockCmd( SPI_PORT_CLK, ENABLE );                     //SPI2 3在APB1上
    }

    SPI_Cmd( SPI_PORT, DISABLE );                                           //关闭SPI外设，配置前关闭
    SpiInitStructer.SPI_Direction = SPI_Direction_2Lines_FullDuplex;        //双线全双工
    SpiInitStructer.SPI_Mode = SPI_Mode_Master;                             //主机模式
    SpiInitStructer.SPI_CPOL = SPI_CPOL_Low;                                //空闲状态为低电平 
    SpiInitStructer.SPI_CPHA = SPI_CPHA_1Edge;                              //第一个边沿采集数据
    SpiInitStructer.SPI_DataSize = SPI_DataSize_8b;                         //8位数据
    SpiInitStructer.SPI_NSS = SPI_NSS_Soft;                                 //从机软件管理
    SpiInitStructer.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_64;       //64分频
    SpiInitStructer.SPI_FirstBit = SPI_FirstBit_MSB;                        //最高位先发送
    SpiInitStructer.SPI_CRCPolynomial = 7;                                  //CRC多项式,默认不使用SPI自带CRC     
    SPI_Init( SPI_PORT, &SpiInitStructer );
    SPI_Cmd( SPI_PORT, ENABLE );                                            //打开SPI外设，配置后开启
}

//SPI收发一个字节
uint8_t drv_spi_read_write_byte( uint8_t TxByte )
{
    uint8_t l_Data = 0;
    uint16_t l_WaitTime = SPI_WAIT_TIMEOUT;

    //等待发送缓冲区为空
    while((RESET == SPI_I2S_GetFlagStatus( SPI_PORT, SPI_I2S_FLAG_TXE )) && ( l_WaitTime-- ));
    //发送数据
    SPI_PORT->DR = TxByte;

    //重新设置接收等待时间
    l_WaitTime = SPI_WAIT_TIMEOUT;
    //等待接收缓冲区非空
    while(( RESET == SPI_I2S_GetFlagStatus( SPI_PORT, SPI_I2S_FLAG_RXNE )) && (l_WaitTime--));
    //读取接收数据
    l_Data = (uint8_t)SPI_PORT->DR;
    //返回
    return l_Data;
}
#endif

#ifdef __USE_SOFT_SPI_INTERFACE__                                           /** 软件SPI */

void drv_spi_init( void )
{
    GPIO_InitTypeDef    SpiGpioInitStructer;

    RCC_APB2PeriphClockCmd( SPI_CLK_GPIO_CLK | SPI_MISO_GPIO_CLK | SPI_MOSI_GPIO_CLK | SPI_NSS_GPIO_CLK, ENABLE );
    
    RCC_APB2PeriphClockCmd( RCC_APB2Periph_AFIO, ENABLE );                  //使能AFIO时钟，这样才能重映射
    GPIO_PinRemapConfig( GPIO_Remap_SWJ_JTAGDisable, ENABLE);               //关闭JTAG，这样才能腾出PA15,PB3,PB4
    GPIO_PinRemapConfig( GPIO_Remap_SPI1, ENABLE);                          //将SPI1，重映射

    SpiGpioInitStructer.GPIO_Speed = GPIO_Speed_10MHz;
    SpiGpioInitStructer.GPIO_Mode = GPIO_Mode_Out_PP;                       //SCK MOSI NSS配置为推挽输出
    
    SpiGpioInitStructer.GPIO_Pin = SPI_CLK_GPIO_PIN;
    GPIO_Init( SPI_CLK_GPIO_PORT, &SpiGpioInitStructer );                   //初始化SCK
    GPIO_ResetBits( SPI_CLK_GPIO_PORT, SPI_CLK_GPIO_PIN);                   //初始化状态设置为低
    
    SpiGpioInitStructer.GPIO_Pin = SPI_MOSI_GPIO_PIN;
    GPIO_Init( SPI_MOSI_GPIO_PORT, &SpiGpioInitStructer );                  //初始化MOSI
    GPIO_SetBits( SPI_MOSI_GPIO_PORT, SPI_MOSI_GPIO_PIN);                   //初始化状态设置为高
    
    SpiGpioInitStructer.GPIO_Pin = SPI_NSS_GPIO_PIN;
    GPIO_Init( SPI_NSS_GPIO_PORT, &SpiGpioInitStructer );                   //初始化NSS
    GPIO_SetBits( SPI_NSS_GPIO_PORT, SPI_NSS_GPIO_PIN);                     //初始化状态设置为高

    SpiGpioInitStructer.GPIO_Mode = GPIO_Mode_IPU;
    SpiGpioInitStructer.GPIO_Pin = SPI_MISO_GPIO_PIN;
    GPIO_Init( SPI_MISO_GPIO_PORT, &SpiGpioInitStructer );                  //初始化MISO 上拉输入
    GPIO_SetBits( SPI_MISO_GPIO_PORT, SPI_MISO_GPIO_PIN);                   //初始化状态设置为高
}


//收发一个字节
uint8_t drv_spi_read_write_byte( uint8_t TxByte )
{
    uint8_t i = 0, Data = 0;
    
    spi_set_clk_low( );
    
    for( i = 0; i < 8; i++ )                                    //一个字节8byte需要循环8次
    {
        /** 发送 */
        if( 0x80 == ( TxByte & 0x80 ))
        {
            spi_set_mosi_hight( );                              //如果即将要发送的位为 1 则置高IO引脚
        }
        else
        {
            spi_set_mosi_low( );                                //如果即将要发送的位为 0 则置低IO引脚
        }
        TxByte <<= 1;                                           //数据左移一位，先发送的是最高位
        
        spi_set_clk_high( );                                    //时钟线置高
        __nop( );
        __nop( );
        
        /** 接收 */
        Data <<= 1;                                             //接收数据左移一位，先接收到的是最高位
        if( 1 == spi_get_miso( ))
        {
            Data |= 0x01;                                       //如果接收时IO引脚为高则认为接收到 1
        }
        
        spi_set_clk_low( );                                     //时钟线置低
        __nop( );
        __nop( );
    }

    return Data;                                                //返回接收到的字节
}
#endif


